// import type SocketManager from "./manager";
import type ClientManager from "../utils/manager";

/**
 * @author : andy
 * @description : socket类
 */
export interface SocketParams {
  /** socket地址 */
  url: string;
  /** 心跳数据 */
  heartBeat?: string;
  /** 发送心跳间隔时长，默认每隔5秒一次，如果设置为0，那么表示不需要发心跳检测 */
  heartBeatDuration?: number;
  /** 重连次数，默认5次，如果为0，那么表示不限次数，一直会重连，直到成功为止 */
  reconnectCount?: number;
  /** 是否需要发送心跳检测，默认不需要发送心跳检测 */
  needHeartBeat?: boolean;
  /** 是否需要重连 */
  needReconnect?: boolean;
}
type SocketCallback = (evt: MessageEvent) => void;
export default class Socket {
  /** socket实例的唯一标识 */
  id: string;
  /** socket实例 */
  private socket?: WebSocket;
  /** socket管理对象 */
  private socketManager: ClientManager<Socket>;
  /** 创建socket实例参数 */
  private params: SocketParams;
  /** 心跳定时器id */
  private heartBeatTimer?: number;
  /** 接收消息的回调函数 */
  private messageCallback?: SocketCallback;
  /** 异常回调函数 */
  private errorCallback?: SocketCallback;
  /** 连接成功的回调函数 */
  private openCallback?: SocketCallback;
  /** 关闭的回调函数 */
  private closeCallback?: SocketCallback;
  /** 重连次数计数器 */
  private reconnectCount: number = 0;
  /** socket是否已经关闭，超过重连次数 */
  private isClosed: boolean = false;
  constructor (params: SocketParams , socketManager: ClientManager<Socket>) {
    this.id = Date.now().toString(16);
    this.socketManager = socketManager;
    const defaultParams: SocketParams = {
      heartBeat : '',
      reconnectCount : 5,
      url : '',
      heartBeatDuration : 5000,
      needHeartBeat : false,
      needReconnect : true
    };
    this.params = Object.assign({} , defaultParams , params);
    this.initSocket();
  }
  private initSocket () {
    const { url } = this.params;
    if (!url) {
      console.warn('url不能为空！');
      return
    }
    this.socket = new WebSocket(url);
    this.bindEvents();
  }
  private bindEvents () {
    if (this.socket) {
      this.socket.addEventListener('close' , this._onClose.bind(this));
      this.socket.addEventListener('error' , this._onError.bind(this));
      this.socket.addEventListener('message' , this._onMessage.bind(this));
      this.socket.addEventListener('open' , this._onOpen.bind(this));
    }
  }
  private _onClose (evt: Event) {
    if (this.closeCallback) {
      this.closeCallback(evt as MessageEvent);
    }
  }
  private _onError (evt: Event) {
    const { needReconnect } = this.params;
    // 如果需要重连，那么先重连，如果不需要重连，报错的话，就删除这个socket实例
    if (needReconnect) {
      if (!this.isClosed) {
        this.reconnect();
      }
    } else {
      this.socketManager.delete(this.id);
    }
    if (this.errorCallback) {
      this.errorCallback(evt as MessageEvent);
    }
  }
  private _onMessage (evt: Event) {
    if (this.messageCallback) {
      this.messageCallback(evt as MessageEvent);
    }
  }
  private _onOpen (evt: Event) {
    if (this.openCallback) {
      this.openCallback(evt as MessageEvent);
    }
  }
  /**
   * 发送心跳数据
   */
  sendHeartBeat () {
    const { heartBeat , heartBeatDuration , needHeartBeat } = this.params;
    if (!needHeartBeat) {
      return;
    }
    if (this.heartBeatTimer) {
      clearInterval(this.heartBeatTimer);
    }
    this.heartBeatTimer = setInterval(() => {
      if (this.socket) {
        if (heartBeat) {
          this.socket.send(heartBeat);
        } else {
          clearInterval(this.heartBeatTimer);
          this.socketManager.delete(this.id);
          throw new Error(`没有心跳数据：${heartBeat}`);
        }
      }
    } , heartBeatDuration);
  }
  /**
   * 重连socket
   */
  private reconnect () {
    const { reconnectCount , needReconnect } = this.params;
    this.reconnectCount++;
    if (this.reconnectCount >= reconnectCount! && needReconnect) {
      // 超过重连次数，关闭socket
      this.disconnect();
      this.isClosed = true;
      this.socketManager.delete(this.id);
      return;    
    }
    // 重新初始化连接
    this.initSocket();
  }
  /**
   * 关闭连接
   */
  disconnect () {
    this.socket?.close();
  }
  onMessage (callback: SocketCallback) {
    this.messageCallback = callback;
  }
  onError (callback: SocketCallback) {
    this.errorCallback = callback;
  }
  onOpen (callback: SocketCallback) {
    this.openCallback = callback;
  }
  onClose (callback: SocketCallback) {
    this.closeCallback = callback;
  }
}